package com.austin.framework.cacheableExtend;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.austin.framework.cacheableExtend.helper.CacheHelper;
import com.austin.framework.cacheableExtend.helper.ThreadTaskHelper;
import com.austin.framework.cacheableExtend.model.CacheInvocation;
import com.austin.framework.cacheableExtend.utils.LockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;

import java.util.concurrent.TimeUnit;

/**
 * @author: austin
 * @since: 2023/2/18 19:00
 */
@Slf4j
public class CustomRedisCache extends RedisCache {

    protected CustomRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {
        super(name, cacheWriter, cacheConfig);
    }


    @Override
    public void put(Object key, Object value) {
        super.put(key, value);
    }

    /**
     * 重写Redis Cache接口的get()方法，获取key的ttl，判断当缓存key的剩余的时间<=我们配置的刷新时间(preloadTimeSecond)，就手动刷新缓存，为了不影响get的性能，启用后台异步线程去完成缓存的刷新。
     */
    @Override
    @Nullable
    public ValueWrapper get(Object key) {
        RedisTemplate redisTemplate = CacheHelper.getCacheManager().getCacheRedisTemplate();
        Object obj = redisTemplate.opsForValue().get(createCacheKey(key));
        ValueWrapper valueWrapper = toValueWrapper(obj);
        long preloadTimeSecond = 0;
        try {
            CacheInvocation cachedInvocation = CacheHelper.getCacheManager().getCacheInvocation();
            preloadTimeSecond = cachedInvocation.getMetaData().getPreloadTimeSecond();
        } catch (Exception e) {
            log.error("Get customCacheable preloadTimeSecond fail, {}", e);
        }
        if (ObjectUtil.isNotEmpty(valueWrapper) && preloadTimeSecond > 0) {
            String cacheKey = createCacheKey(key);
            Long ttl = redisTemplate.getExpire(cacheKey, TimeUnit.SECONDS);
            if (ObjectUtil.isNotEmpty(ttl) && ttl <= preloadTimeSecond) {
                log.info("Redis cacheKey：{}, ttl: {}, preloadTimeSecond: {}", cacheKey, ttl, preloadTimeSecond);
                String lockKey = "lockKey:" + super.getName() + ":" + createCacheKey(key);
                LockService lockService = SpringUtil.getApplicationContext().getBean("lockService", LockService.class);
                boolean success = lockService.grabLock(lockKey, 3, TimeUnit.SECONDS);
                ThreadTaskHelper.execute(() -> {
                    if (success) {
                        try {
                            CacheHelper.refreshCache(super.getName());
                        } catch (Exception e) {
                            log.error("refreshCacheError: {}", e);
                        } finally {
                            lockService.releaseLock(lockKey);
                        }
                    }
                });
            }
        }
        return valueWrapper;
    }
}
